/**
 * 
 */
package com.ctrip.cap.device.ios.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctrip.cap.device.exceptions.IOSDeviceException;
import com.ctrip.cap.device.ios.IOSDevice;

/**
 * @author zwxu
 *
 */
public class IPhoneDeviceManager {
	private static final Logger logger = LoggerFactory
			.getLogger(IPhoneDeviceManager.class);

	private Map<String, IPhoneDevice> connectedDevices = new HashMap<>();

	/**
	 * Initializes the ios device.
	 * 
	 * @throws IOException
	 */
	protected void initializeConnection() {

		String udid = getAvailableDevices();
		killAllInstruments();
		killAllSimulators();
		connectedDevices.put(udid, new IPhoneDevice(udid));
	}

	public Set<IOSDevice> getIOSDevices() {
		Set<IOSDevice> devices = new TreeSet<IOSDevice>(
				connectedDevices.values());
		return devices;
	}

	private String getInstrumentsPath() {
		Process process = null;
		String instrumentsPath = null;
		try {
			process = Runtime.getRuntime().exec("xcrun -find instruments");
			BufferedReader br = new BufferedReader(new InputStreamReader(
					process.getInputStream()));

			try {
				process.waitFor();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (process.exitValue() == 0) {
				instrumentsPath = br.readLine().trim();
			}
			if (process.exitValue() != 0 || instrumentsPath == null
					|| instrumentsPath.equals("")) {
				logger.error("Could not find instruments binary. Please ensure `xcrun -find instruments` can locate it.");
			}
			logger.info("Instruments is at: " + instrumentsPath);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			process.destroy();
		}

		return instrumentsPath;
	}

	private String getAvailableDevices() {
		logger.info("Getting list of devices instruments supports");
		Process process = null;
		String line = null;

		String instrumentsPath = getInstrumentsPath();

		try {
			process = Runtime.getRuntime()
					.exec(instrumentsPath + " -s devices");

			try {
				process.waitFor();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			BufferedReader stdout = new BufferedReader(new InputStreamReader(
					process.getInputStream()));
			BufferedReader stderr = new BufferedReader(new InputStreamReader(
					process.getErrorStream()));

			if (process.exitValue() != 0) {
				logger.error("Failed getting devices. Err: "
						+ stderr.readLine() + ". Stdout: " + stdout.readLine()
						+ ".");
			}

			if (process.exitValue() == 0) {
				while ((line = stdout.readLine()) != null) {
					Pattern pattern = Pattern.compile("^.*(iPhone|iPad).*$");
					Matcher match = pattern.matcher(line);
					if (match.matches()) {
						pattern = Pattern.compile("^((?!Simulator).)*$");
						match = pattern.matcher(line);

						if (match.matches()) {
							line = line.substring(line.indexOf('[') + 1,
									line.indexOf(']'));
							logger.info("Getted the udid of real ios device:"
									+ line);
							return line;
						}
					}
				}
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		throw new IOSDeviceException("Conldn't find any real ios device. ");
	}

	private void killAllInstruments() {
		logger.info("Killall instruments");
		try {
			Runtime.getRuntime().exec("pkill -9 -f instruments");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void killAllSimulators() {
		Double xcodeVersion = getXcodeVersion();

		try {
			if (xcodeVersion >= 6) {
				logger.info("Killall iOS Simulator");
				Runtime.getRuntime().exec("pkill -9 -f \"iOS Simulator\"");
			} else {
				logger.info("Killall iPhoneSimulator");
				Runtime.getRuntime().exec("pkill -9 -f iPhoneSimulator");
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private Double getXcodeVersion() {
		Process process = null;
		String outLine = null;
		Double version = null;
		try {
			process = Runtime.getRuntime().exec("xcodebuild -version");
			BufferedReader br = new BufferedReader(new InputStreamReader(
					process.getInputStream()));

			try {
				process.waitFor();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (process.exitValue() == 0) {
				outLine = br.readLine().trim();
			}
			if (process.exitValue() != 0 || outLine == null
					|| outLine.equals("")) {
				logger.error("Could not find Xcode version. Please ensure `xcodebuild -version` can locate it.");
			}

			String[] strList = outLine.split(" ");
			if (strList.length == 2) {
				String[] strs = StringUtils.split(strList[1], ".");
				if (strs.length > 2) {
					version = Double.parseDouble(StringUtils
							.substringBeforeLast(strList[1].toString(), "."));
				} else {
					version = Double.parseDouble(strList[1].toString());
				}
			} else {
				logger.error("Fail to Get Xcode version.");
			}
			logger.info("Xcode Version is: " + version.toString());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return version;
	}

	/**
	 * Shutdown the AndroidDebugBridge and clean up all connected devices.
	 */
	public void shutdown() {

		killAllInstruments();
		killAllSimulators();

		logger.info("stopping Device Manager");
	}

	/**
	 * used with caution or don't call this method
	 */
	public void shutdownForcely() {
		killAllInstruments();
		killAllSimulators();
	}
}
